/* 
 * Server
 * 
 * main.c 
 * 
 * Cree par François Gautrais le 3/04/2011 à 00:09
 * under GPL
 * */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> /* close */
#include <netdb.h> /* gethostbyname */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define SIZE_THREAD 100
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(s) close(s)
#define PORT 2222
#define N_CLIENT_MAX 10
#define FILE_PORT "port.prt"
#define PORT_ERROR -1
#define ERROR -1
#define OK 0
#define SIZE 100
#define TIME_TO_WAIT 50//en ms
#define VRAI 1
#define FAUX 0

typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
typedef struct in_addr IN_ADDR;

typedef struct
{
    int port;
    SOCKET server;
    SOCKADDR_IN sin;
    pthread_mutex_t *mutex;
    int n_client;
}SERVER;

typedef struct
{
	SERVER *server;
	SOCKET client;
	SOCKADDR_IN sin;
	int id;
	//pthread_t reception;
	pthread_t emission;
	int sinsize;
	short job;//0 si rien à faire 
	char commande[SIZE];
}CLIENT;

void *recieve(void *arg){
	SOCKET *client = (SOCKET *) arg;
	char buffer[SIZE];
	while(1){
		if(recv(*client, buffer, SIZE, 0) == SOCKET_ERROR ){
			perror("recv()");
			exit(EXIT_FAILURE);
		}
		printf("le client envoi: %s\n", buffer);
		
	}
}

/* cette fonction permet de creer la socket server, et de la lier au port */ 
int creer_socket_server(SERVER *server){
	server->server = socket(AF_INET, SOCK_STREAM, 0);
	if(server->server == INVALID_SOCKET)
	{
		perror("socket()");
		return ERROR;
	}
	
	server->sin.sin_addr.s_addr = htonl(INADDR_ANY);
	server->sin.sin_family = AF_INET;
	server->sin.sin_port = htons(server->port);

	if(bind(server->server, (SOCKADDR *) &server->sin, sizeof(server->sin))){
		perror("bind()");
		return ERROR;
	}
	
	return OK;
}

/* cette fonction permet de recuperer le port, soit si il est donne en argument,
 * soit si le fichier FILE_PORT existe, il permet d'afficher l'aide.
 * Si il rencontre une erreur il renvoie -1*/
int traiter_arguments(int args, char **argv){
	if (args>1)
		if(!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-?") ){
			//print_help();
		}
		else return atoi(argv[1]);
	else{
		char str[SIZE];
		FILE *f = fopen(FILE_PORT,"r");
		if(f == NULL){
			perror("fopen()");
			return PORT_ERROR;
		}
		fscanf(f, "%s", str);
		return atoi(str);
	} 
		
}

SERVER init_server(int args, char **argv){
	//initialisation des variable
	printf("Initialisation des variables ... \n");
	SERVER server;
	server.port=-1;
	server.sin=(SOCKADDR_IN){0};
	server.n_client=0;
	printf("Effectue\n\n");

	//on recupere le port
	printf("Lecture des argument, et recuperation du port ... \n");
	server.port = traiter_arguments(args, argv);
	if(server.port == PORT_ERROR)	exit(EXIT_FAILURE);
	printf("Effectue\n\n");
	
	//on cree le socket et le sockaddr
	printf("Creation du server ... \n");
	if(creer_socket_server(&server) == ERROR){
		perror("creer_socket_server()");
		exit(EXIT_FAILURE);
	}
	printf("Effectue\n\n");
	
	return server; 
	
}

int compare(char *reference, char *str, int debut, int longueur){
	int i;
	for(i=0; i<longueur; i++)
		if(str[i+debut] != reference[i])
			return FAUX;
	return VRAI;
}

void decaler(char *str, int k){
	int i, l=strlen(str);
	if(l-k>=0)
		for(i=k; i<l; i++)
			str[i-k]=str[i];
}

void fermer_connection(CLIENT *client){
	shutdown(client->client, 2);
	closesocket(client->client);
	client->server->n_client--;
}

void traiter_commande_server(char commande[SIZE] ,CLIENT *client){
	printf("Commande reconnu:\n"); 
	if(compare("say", commande, 0, 3)) printf("say : %s", commande); 
	else if(compare("close", commande, 0, 4))	fermer_connection(client);
	else if(compare("give_id", commande, 0, 7))  printf("close : %s", commande);
}

void *connection_avec_client(void *arg){
	CLIENT *me = (CLIENT *) arg;
	int continuer = 1;
	char buffer[SIZE]="salut tout le monde !!!!!";
	
	while(continuer){		
		int job;
		do{
			pthread_mutex_lock(me->server->mutex);
				job = me->job;
				usleep(TIME_TO_WAIT*1000);
			pthread_mutex_unlock(me->server->mutex);
		}while(!job);
		
		pthread_mutex_lock(me->server->mutex);
			me->job=0;
			strcpy(buffer, me->commande);
		pthread_mutex_unlock(me->server->mutex);
		//on arrive ici ssi on a un job à accomplir

		traiter_commande_server(buffer,me);		
	}
}

void attente_nouveau_client(SERVER *server, CLIENT clients[N_CLIENT_MAX]){
	while(1){
		if(server->n_client<=N_CLIENT_MAX){
			
			printf("Initialisation des information du futur client ...\n");
			CLIENT *client = &client[server->n_client];
			client->id=0;
			client->sin=(SOCKADDR_IN){0};
			client->server=server;
			client->job=0;
			printf("Effectue\n\n");
			
			printf("Ecoute du server sur le port %d\n", server->port);
			if( listen(server->server, 5) == SOCKET_ERROR){
				perror("lisent()");
				exit(EXIT_FAILURE);
			}
			printf("Effectue\n\n");
			
			client->sinsize=sizeof(client->sin);
			
			printf("Attente de connexion d'un client ...\n");
			client->client = accept(server->server, (SOCKADDR *) &client->sin, &client->sinsize);
			
			if(client->client == INVALID_SOCKET){
				printf("BUG\n");
				perror("accept()");
				exit(EXIT_FAILURE);
			}
			client->server->n_client++;
			
			//création
			client->emission = pthread_create(&client->emission, NULL, connection_avec_client, (void*)client);
		}else{
			printf("Le serveur est plein.\n");
			usleep(10*TIME_TO_WAIT*1000);
		}
	}
}



int main(int args, char **argv){
	CLIENT clients[N_CLIENT_MAX];
	SERVER server = init_server(args,argv);	
	attente_nouveau_client(&server, clients);

	
	closesocket(server.server);
	
}
